1. Назначение
MotorStep V6 — прошивка для управления поворотным столом на шаговом моторе через драйвер TB6600. Предназначена для производственных задач, где требуется:
- Точное позиционирование стола в диапазоне 0…360°
- Плавное движение (трапециевидный профиль скорости) для тяжёлых столов (50–1500 кг)
- Автоматический поиск нуля (концевик) при старте
- Ручное управление через энкодер и кнопки
- Внешнее управление через Serial (Console) — в дальнейшем Wi-Fi/WebSocket
- Обратная связь по углу с оптического энкодера MBL3806
MotorStep_v6.ino. Классы не знают друг о друге — только через main.
2. Возможности
🎯 Точность
0.1° по MBL3806 (24000 импульсов на оборот стола при GEAR_RATIO=10)
⚡ Плавность
Трапециевидный профиль: разгон, плато, торможение. Настройка через NVS.
🏠 Автопоиск нуля
4 этапа: отход, быстрый поиск, откат, медленный подход. Контроль по 405°.
🛑 Мгновенный stop
Остановка за один шаг (≤800 мкс). Без инерции, без «догонов».
📟 Дисплей TM1637
3 значения: цель, угол стола, MBL3806. Режимы d1/d2. Индикация Run, EROR, BLOC/OFF.
💾 NVS-память
Сохранение шага, флага findHome, accel/cruise/start в энергонезависимую память.
🔄 Протокол Console
Команды move/stop/home/... + запросы status?/motor?/mbl?. Ответы OK/BUSY/DONE/READY.
🎛️ Инверсия MBL
Переключение направления через #define POSITION_INVERTED — без перепайки.
3. Подключение элементов
3.1. Карта пинов
| GPIO | Функция | Что подключено | Примечание |
|---|---|---|---|
0 | EncoderHW040 CLK | HW-040 CLK | ISR, CHANGE |
1 | EncoderHW040 DT | HW-040 DT | ISR, CHANGE |
2 | Кнопка дисплея | Кнопка (на GND) | Strapping — свой опрос |
3 | EncoderHW040 SW | HW-040 SW | GyverButton |
4 | TM1637 CLK | Дисплей TM1637 | — |
5 | TM1637 DIO | Дисплей TM1637 | — |
6 | TB6600 STEP | TB6600 PUL+ | Импульсы шага |
7 | TB6600 DIR | TB6600 DIR+ | Направление |
8 | Концевик HOME | Механический/индуктивный | INPUT_PULLUP, LOW = сработал |
9 | Резерв | — | Свободен |
10 | TB6600 ENA | TB6600 ENA+ | LOW = мотор включён |
20 | MBL3806 A | MBL3806 A | UART0 RX — 1 кОм, INPUT_PULLUP |
21 | MBL3806 B | MBL3806 B | UART0 TX — 1 кОм, INPUT_PULLUP |
3.2. Схема соединений
TB6600 (драйвер шагового мотора)
ESP32-C3 TB6600
─────────────────────────────
GPIO 6 ──── 1кОм ──── PUL+ (STEP)
GPIO 7 ──── 1кОм ──── DIR+
GPIO 10 ──── 1кОм ──── ENA+
GND ────────────── PUL- / DIR- / ENA- (общий)
MBL3806 (оптический энкодер на моторе)
ESP32-C3 MBL3806
─────────────────────────────
+5V ────────────── VCC (красный)
GND ────────────── GND (чёрный)
GPIO 20 ──── 1кОм ──── A (зелёный)
GPIO 21 ──── 1кОм ──── B (белый)
HW-040 (энкодер ручного управления)
ESP32-C3 HW-040
─────────────────────────────
+3.3V ────────────── VCC
GND ────────────── GND
GPIO 0 ────────────── CLK
GPIO 1 ────────────── DT
GPIO 3 ────────────── SW (кнопка)
TM1637 (дисплей)
ESP32-C3 TM1637
─────────────────────────────
+5V ────────────── VCC
GND ────────────── GND
GPIO 4 ────────────── CLK
GPIO 5 ────────────── DIO
Кнопка дисплея
ESP32-C3
─────────────────────────────
GPIO 2 ──[Кнопка]── GND
Концевик HOME
ESP32-C3
─────────────────────────────
GPIO 8 ──[Концевик NO]── GND
При срабатывании замыкается на GND → LOW. В покое — HIGH (INPUT_PULLUP).
3.3. Питание
| Потребитель | Напряжение | Ток | Источник |
|---|---|---|---|
| ESP32-C3 Mini | 5 В (USB) | ~250 мА | USB или БП |
| TB6600 (логика) | 5 В | ~20 мА | БП 5 В |
| TB6600 (мотор) | 24–48 В | до 3 А | Отдельный БП |
| MBL3806 | 5 В | ~50 мА | БП 5 В |
| TM1637 | 5 В | ~30 мА | БП 5 В |
| HW-040 | 3.3 В | ~5 мА | ESP32 |
4. Настройки
4.1. Пины (в коде)
Пины заданы в соответствующих .cpp файлах классов:
| Класс | Файл | Что настраивается |
|---|---|---|
| Display | Display.cpp | PIN_CLK_DISP=4, PIN_DIO_DISP=5 |
| Buttons | Buttons.cpp | PIN_ENC_SW=3, PIN_DISP_BTN=2, PIN_HOME=8 |
| EncoderHW040 | EncoderHW040.cpp | PIN_ENC_CLK=0, PIN_ENC_DT=1 |
| Position | Position.cpp | PIN_MBL_A=20, PIN_MBL_B=21 |
| Motor | Motor.cpp | PIN_STEP=6, PIN_DIR=7, PIN_ENA=10 |
4.2. Механика (в коде)
Motor.cpp — в начале файла
static const int32_t STEPS_PER_REV = 800; // шагов мотора на оборот (1/4 микрошаг)
static const float GEAR_RATIO = 10.0; // ← РЕДУКТОР (мотор:стол = 10:1)
static const int32_t ANGLE_MIN = 0;
static const int32_t ANGLE_MAX = 360;
Параметры плавности (в Motor, дефолты — в Settings)
| Параметр | Дефолт | Диапазон | Что значит |
|---|---|---|---|
accel | 1000 | 1..2000 | Шагов на разгон/торможение |
cruise | 800 | 50..10000 мкс | Задержка на плато (макс. скорость) |
start | 5000 | 100..20000 мкс | Задержка на старте/стопе |
Position.cpp — инверсия
#define POSITION_INVERTED 0 // 0 = прямой, 1 = инверсный (A/B поменяны)
4.3. NVS (энергонезависимая память)
Сохраняются в NVS (namespace "motorstep"):
| Ключ | Тип | Дефолт | Что значит |
|---|---|---|---|
stepIdx | int | 4 | Индекс шага (0..4 → 180°/90°/10°/5°/1°) |
findHome | bool | true | Искать ноль при старте |
accel | int | 1000 | Параметр плавности |
cruise | int | 800 | Параметр плавности |
start | int | 5000 | Параметр плавности |
Управление через команды accel/cruise/start/save/load (см. раздел 5.1).
5. Управление
5.1. Console — команды (Serial)
Скорость: 115200. Line Ending: Newline или Both NL & CR.
Действия (без "?")
| Команда | Что делает | Ответ |
|---|---|---|
move <angle> | Перейти к углу (0..360) | OK / BUSY |
home | Поиск нуля | OK |
reset | Сброс позиции в 0 | OK |
hold | Удержание/освобождение мотора | OK |
stop | Мгновенный стоп | OK |
stop! | Стоп с ошибкой | OK |
clear | Сброс ошибки | OK |
step <idx> | Установить индекс шага (0..4) | OK |
next | Следующий шаг | OK |
accel <n> | Установить accel (сохр. в NVS) | OK |
cruise <n> | Установить cruise (сохр. в NVS) | OK |
start <n> | Установить start (сохр. в NVS) | OK |
save | Сохранить в NVS | OK |
load | Загрузить из NVS | OK |
hb <0|1> | Heartbeat вкл/выкл | OK |
help | Список команд | OK + help |
Запросы (с "?")
| Команда | Ответ |
|---|---|
ready? | READY / BUSY |
status? | IDLE / MOVING |
angle? | Целевой угол |
motor? | Угол стола (факт) |
mbl? | Угол MBL3806 |
step? | Индекс шага (0..4) |
error? | OK / ERROR |
hold? | ON / OFF |
hb? | ON / OFF |
accel? / cruise? / start? | Значение параметра |
Асинхронные события (ESP → ПК)
| Событие | Когда |
|---|---|
DONE | Мотор завершил движение |
READY | Контроллер свободен |
PROGRESS: <angle> | Каждый 1° во время движения (если включено) |
HB | Heartbeat (если включён) |
Serial.println блокирует loop на ~1 мс, что даёт рывки. В V6 по умолчанию PROGRESS может быть закомментирован — рекомендую не использовать во время движения.
5.2. Кнопки
| Кнопка | Действие | Что происходит |
|---|---|---|
| HW-040 SW (GPIO 3) | Короткий клик | Смена шага (180° → 90° → 10° → 5° → 1° → 180°) |
| HW-040 SW | Двойной клик | Удержание мотора (BLOC / OFF) |
| HW-040 SW | Длинное удержание | Сброс позиции (motor + position + target = 0). Если ошибка — сброс ошибки |
| GPIO 2 | Клик | Переключение режима дисплея (d1 ↔ d2) |
5.3. Энкодер HW-040
Вращение энкодера накапливает целевой угол. После паузы (1 сек) — мотор плавно едет к цели.
| Действие | Что происходит |
|---|---|
| Вращение вправо | _targetAngle += step, дисплей показывает цель |
| Вращение влево | _targetAngle -= step |
| Пауза 1 сек | motor.moveTo(_targetAngle) — плавное движение |
5.4. Дисплей TM1637
Режимы (переключаются кнопкой GPIO 2)
| Режим | Индикатор | Формат | Что показывает |
|---|---|---|---|
| d1 | d 1 | XXX | Угол стола (факт мотора) |
| d2 | d 2 | _XXX | Угол MBL3806 (с подчёркиванием) |
Приоритет отображения (по убыванию)
- EROR — мигает при ошибке
- Run — во время движения мотора
- S XX — 1 сек после смены шага
- BLOC / OFF — 1 сек после переключения удержания
- d1 / d2 — 1 сек после переключения режима
- Цель — 1 сек после вращения энкодера
- Угол — основное (d1: мотор, d2: MBL)
Специальные сообщения
| Текст | Что значит |
|---|---|
Run | Мотор движется (R большая, u n маленькие) |
EROR | Ошибка (мигает) |
S 90 | Выбран шаг 90° |
BLOC | Мотор удерживается |
OFF | Мотор освобождён (можно крутить руками) |
d 1 / d 2 | Режим дисплея (угол стола / MBL) |
6. Как компилировать
6.1. Плата
| Параметр | Значение |
|---|---|
| Board | ESP32C3 Dev Module |
| Чип | ESP32-C3 |
| Плата | ESP32-C3 Mini V1 (или аналог) |
6.2. Библиотеки
Установить через Library Manager (Sketch → Include Library → Manage Libraries):
| Библиотека | Автор | Для чего |
|---|---|---|
| TM1637Display | Avishay Orpaz | Дисплей |
| GyverButton | AlexGyver | Кнопка энкодера (SW) |
| GyverEncoder | AlexGyver | НЕ используется в V6 (только для справки) |
EncoderHW040 написан вручную (ISR + квадратурный декодер). Библиотека GyverEncoder не требуется.
6.3. Настройки Arduino IDE (Tools)
| Настройка | Значение | Почему |
|---|---|---|
| Board | ESP32C3 Dev Module | Правильный чип |
| USB CDC On Boot | Enabled | Serial через USB (Console работает) |
| CPU Frequency | 160 MHz | Скорость |
| Flash Size | 4MB (32Mb) | Стандарт |
| Upload Speed | 921600 | Быстрая прошивка |
| JTAG Adapter | Disabled | Не занимает GPIO 0-5 |
| USB DFU On Boot | Disabled | Не нужно |
6.4. Прошивка
- Подключить ESP32-C3 через USB
- Выбрать порт в Tools → Port
- Нажать Upload (Ctrl+U)
- Если не прошивается: зажать кнопку BOOT на плате, нажать RESET, отпустить RESET, отпустить BOOT. Затем Upload
- После прошивки — нажать RESET
7. Известные проблемы и особенности
Плавность vs Console
Serial-вывод (Serial.println) блокирует loop на ~1 мс. Это вызывает рывки мотора. Решение: не выводить PROGRESS во время движения; используйте запрос motor? после DONE.
Подергивание при активном дисплее
TM1637 — I2C-подобный протокол (не аппаратный). Каждое обновление ~1-2 мс. Решение: обновлять дисплей только когда мотор не движется (display.setRun(motorBusy) уже это делает).
Пропуски энкодера HW-040
Механический энкодер даёт дребезг. Решение: антидребезг 2000 мкс в ISR + REPORT_MIN_INTERVAL_MS = 50 мс. Редкие пропуски возможны.
Двойное срабатывание кнопки GPIO 2
Strapping-пин, дешёвая кнопка. Решение: state machine в Buttons.cpp, антидребезг 50 мс.
Конфликт GPIO 20/21 (UART0)
GPIO 20/21 — это UART0 (RX/TX). Serial для Console использует USB CDC (виртуальный COM-порт), который не занимает эти пины. Поэтому MBL3806 работает на них.
8. Проверено и однозначно не работает
| Что | Результат | Причина |
|---|---|---|
| GyverButton на GPIO 2 | НЕ работает | GPIO 2 — strapping-пин. GyverButton не срабатывает стабильно. Заменено на state machine. |
| GyverEncoder на ESP32-C3 | НЕ работает | GyverEncoder писалась под AVR/ESP8266. На C3 работает нестабильно (пропуски, двойные срабатывания). Заменено на собственный ISR-декодер. |
| Точка на TM1637 | НЕ работает | showNumberDecEx не выводит точку (особенность библиотеки/дисплея). Заменено на подчёркивание _ перед числом. |
| GPIO 20/21 как обычные GPIO | НЕ работает с Serial.begin() | Если вызвать Serial.begin() для UART0 (не USB CDC) — GPIO 20/21 заняты. В V6 Serial через USB CDC — конфликта нет. |
| PROGRESS каждые 1° | НЕ работает плавно | Serial.println блокирует loop → рывки мотора. Рекомендуется отключить. |
| Пины 0,1 при загрузке | Осторожно | GPIO 0 — strapping (boot). HW-040 CLK на GPIO 0 — если вращать при загрузке, может помешать. На практике работает. |
| GPIO 3 при загрузке | Осторожно | GPIO 3 — strapping (JTAG source). HW-040 SW на GPIO 3. Если нажать при загрузке — не критично. С JTAG Adapter = Disabled — работает. |
| GPIO 8 при загрузке | Осторожно | GPIO 8 — strapping. Концевик HOME на GPIO 8. Должен быть HIGH при загрузке (концевик разомкнут). Если концевик NC — не запустится. |
| GPIO 9 при загрузке | ОК | Strapping (BOOT). Не используется. Свободен. |
9. Состав проекта
MotorStep_v6/
├── MotorStep_v6.ino ← main (дирижёр)
├── Display.h / Display.cpp ← TM1637 (3 значения, режимы, индикация)
├── Buttons.h / Buttons.cpp ← HW-040 SW + GPIO 2 + концевик
├── EncoderHW040.h / EncoderHW040.cpp ← HW-040 (свой ISR-декодер)
├── Settings.h / Settings.cpp ← NVS (step, findHome, accel, cruise, start)
├── Position.h / Position.cpp ← MBL3806 (ISR + GEAR_RATIO + инверсия)
├── Motor.h / Motor.cpp ← шаговый мотор (трапеция, findHome, stop)
└── Console.h / Console.cpp ← Serial-протокол (команды/ответы)
Зависимости между классами
Классы не знают друг о друге. Все связи — через main:
main → motor.moveTo(angle) ← main передаёт данные
main → display.setMotorAngle() ← main передаёт данные
main → settings.getStep() ← main читает
main → position.getAngle() ← main читает
main → console.sendDone() ← main отправляет события